每个 iOS 程序员都要时不时的为他们的 APP 做 Debug。除非你是那种超级大牛,否则你肯定体验过查了无数个小时的 Bug 最后才发现那仅仅是个简单的语法错误时那种油然而生的绝望感。或者更糟:你根本就没发现那些 Bug。无论你是编程新手,还是开发过很多 APP 的老司机,例行的写写单元测试会让你的代码更可靠,更安全,更容易 Debug!
你很走运,Xcode 7 和 Swift 支持单元测试。尽管单元测试不保证(有了它你就会写出)绝对没有 Bug 的 APP,它还是一种能让你验证每段代码是否如期工作,并让 debug 过程更加便利。
正如其名,在单元测试中你要为某段代码单元创建一些小规模的、针对其某个特性的测试,然后确保每个代码单元都能通过这些测试。如果通过的话,它的旁边会出现一个绿色小标志,而如果因故测试不通过, Xcode 会把该测试标记为 “failed”。这就提示你去查看代码,找出失败原因。
这篇教程将简要介绍如何在 Swift 项目中使用XCTest.framework进行代码单元测试。那么我们为什么需要做单元测试呢?单元测试对于我们有以下几点帮助:
帮助理解需求,单元测试应该反映 Use Case,把被测单元当成黑盒测试其外部行为。
提高实现质量,单元测试不保证程序做正确的事,但能帮助保证程序正确地做事,从而提高实现质量。
测试成本低,相比集成测试、验收测试,单元测试所依赖的外部环境少,自动化程度高,时间短,节约了测试成本。
反馈速度快,单元测试提供快速反馈,把 Bug 消灭在开发阶段,减少问题流到集成测试、验收测试和用户,降低了软件质量控制的成本。
利于重构,由于有单元测试作为回归测试用例,有助于预防在重构过程中引入 Bug。
文档作用,单元测试提供了被测单元的使用场景,起到了使用文档的作用。
对设计的反馈,一个模块很难进行单元测试通常是不良设计的信号,单元测试可以反过来指导设计出高内聚、低耦合的模块。
默认测试类
首先我们新建一个项目SwiftUnitTest,它将在SwiftUnitTestTests目录下自动创建出一个默认测试类(文件)SwiftUnitTestTests.swift:
  | 
  | 
在这个文件中定义了一个测试类SwiftUnitTestTests,它里面包含了一个setUp()方法和tearDown()方法,分别用来在每个测试方法运行之前做初始化准备,和在测试方法运行之后做清理工作。此外,它还包含了以test开头命名的2个测试方法:testExample()和testPerformanceExample()。
我们需要注意:
- 任何以
test开头命名的的方法都是一个测试方法,在每次单元测试执行时自动执行,它没有返回值; - 在测试方法中,可以使用
self.measureBlock() { }来测量代码的运行时间; - 测试方法执行的顺序跟测试方法名有关,比如
test01()会优先于test02()执行 
通过快捷键CMD+U即可运行当前的单元测试。可以看到所有测试方法已通过。同样,使用CMD+SHIFT+Y打开Console也能看到相应测试方法的运行提示。
定制测试类
为了更好的管理测试用例,我们建议为某个需要测试的类单独创建一个测试类(文件),在这之前,我们先创建一个简单的类用来测试:
  | 
  | 
这个类定义了一种Person类型,它有一个属性叫name,其初始状态为Children。我们可以对其调用transform方法,之后它就变成了GrownMan。
然后我们新建一个针对Person类做测试的测试类:新建文件,选择Test Case Class, 为此类取名为PersonTest(建议使用<待测试类名>Tests的形式):
然后我们删除原来的testExample测试方法,并新建方法:
  | 
  | 
注意:如果此时Xcode提示无法找到
Person的类定义,请在顶部加上@testable import SwiftUnitTest。里面的SwiftUnitTest是当前项目的名称。
在此测试方法中,我们创建出了一个新的Person实例,然后对其调用transform()方法。这时我们使用断言XCTAssert来判断person目前的name属性值是否为GrownMan。XCTAssert是一个全局函数,它的第一个参数为布尔表达式,如果为true表示断言通过;它的第二个参数为断言的描述。
注意:如果实例创建比较复杂,并且需要在多个测试方法中使用。你可以在类中定义对应的实例属性,并且在
setUp()方法中进行初始化,在tearDown()方法中进行资源清理。
运行单元测试
此时执行CMD+U运行单元测试,可以看到单元测试都已通过: 
然后我们再修改原有测试方法testPerformanceExample()中的内容:
  | 
  | 
再次CMD+U执行单元测试,等待几秒钟,可以看到所有测试用例都已通过,打开console可以看到testPerformanceExample()中代码的运行时间:
  | 
  | 
断言测试API列表
  | 
  |